home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2010 April
/
PCWorld0410.iso
/
pluginy Firefox
/
398
/
398.xpi
/
chrome
/
forecastfox.jar
/
content
/
profiles
/
profile-service.js
< prev
next >
Wrap
Text File
|
2010-02-04
|
20KB
|
757 lines
/*------------------------------------------------------------------------------
Copyright (c) 2008 Ensolis, LLC. All Rights Reserved.
----------------------------------------------------------------------------*/
/******************************************************************************
* load a list of preferences into an object. Used in setting up items.
*
* @return An object hold preference names and value.
******************************************************************************/
function getDefaultPrefs()
{
//set the return variable
var rv = {};
var sorter = [];
//get the default pref branch and its children
var branch = getBranch(true, null);
var prefs = branch.getChildList("", {});
//loop through prefs
for (var i=0; i<prefs.length; i++) {
var pref = prefs[i];
//skip excluded
if (EXCLUDED_PREFS.hasOwnProperty(pref) &&
EXCLUDED_PREFS[pref] == true)
continue;
//get the preference value
var value = "";
try {
switch (branch.getPrefType(pref)) {
case Ci.nsIPrefBranch.PREF_INT:
value = branch.getIntPref(pref);
break;
case Ci.nsIPrefBranch.PREF_BOOL:
value = branch.getBoolPref(pref);
break;
case Ci.nsIPrefBranch.PREF_STRING:
default:
try {
value = branch.getComplexValue(pref, Ci.nsIPrefLocalizedString).data;
} catch(e) {
value = branch.getCharPref(pref);
}
break;
}
//add to array
sorter.push({ "name": pref, "value": value});
//value does not exist in default so remove it
} catch (e) {
var userBranch = getBranch(false, null);
if (userBranch.prefHasUserValue(pref))
userBranch.clearUserPref(pref);
}
}
// sort the array and add to object
sorter.sort(sortByName);
while (sorter.length) {
var item = sorter.shift();
rv[item.name] = item.value;
}
//return the variable
return rv;
}
/******************************************************************************
* Interface for describing the profile service. This service should
* be used for all profile data related changes.
* Migration, import, and export is handled by the migrator service.
*
* @status FROZEN
* @version 1.0
******************************************************************************/
function ProfileService()
{
//setup additional interfaces
this._ifaces.push(Ci.nsIObserver);
this._ifaces.push(Ci.nsISupportsWeakReference);
//setup a new error
this._error = Cc["@ensolis.com/forecastfox/error-item;1"].
createInstance(Ci.ffIErrorItem);
}
ProfileService.prototype = {
__proto__: new ServiceBase("ProfileService"),
_obSvc: null,
_items: null,
_rotateTimer: null,
_isBatch: null,
_isRotating: null,
_isLoading: null,
_flushPending: null,
_defaultPrefs: null,
///////////////////////////
// nsIObserver
observe: function ProfileService_observe(aSubject, aTopic, aData)
{
switch (aTopic) {
//timer fired
case "timer-callback":
//rotating fired so switch profiles
if (aSubject == this._rotateTimer) {
this.current = this._nextItem();
this._scheduleTimer();
}
break;
//preference changed
case "nsPref:changed":
//ignore changes if we are loading
if (this.isLoading)
return;
//current changed
if (aData == "profile.current") {
this.current = this.getItem(getPref(aData));
return;
}
//keep properties in sync with prefs
var id = this.current.ID;
this._items[id].setProperty(aData, getPref(aData));
this._flushData();
//rotating prefs changed
if (aData == "profile.switch.enabled" ||
aData == "profile.switch.delay") {
//start rotating if we are not in batch mode
if (!this.isBatch)
this.startRotating();
return;
}
break;
}
},
////////////////////////////////
// ffIService
/**
* Initialize the component. Called by the manager service. Returns false
* if the datasource could not be loaded. Chech the lastError property for
* more information.
*/
start: function ProfileService_start()
{
//setup the variables
this._items = {};
this._isBatch = false;
this._isRotating = false;
this._isLoading = false;
this._flushPending = false;
//get the disk and migrator services
var mgrSvc = Cc["@ensolis.com/forecastfox/manager-service;1"].
getService(Ci.ffIManagerService);
var migSvc = mgrSvc.migrator;
//get the observer service
this._obSvc = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
//setup the timer with 10 minute repeat timer
this._rotateTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
//get the list of preference
this._defaultPrefs = getDefaultPrefs();
//start batch mode
this.startBatch();
//load profiles using the migration service
var success = migSvc.migrate(this);
if (!success) {
var err = migSvc.lastError;
this._error.init(err.severity, err.name, err.message);
}
//end batch mode
this.endBatch();
//setup a preference observer
var pb2 = this.branch.QueryInterface(Ci.nsIPrefBranchInternal);
pb2.addObserver("", this, true);
//return our success code
return success;
},
/**
* Destroy the component. Called by the manager service. This may be
* called prior to start so it needs to be safe.
*/
stop: function ProfileService_stop()
{
//remove the pref observer
var pb2 = this.branch.QueryInterface(Ci.nsIPrefBranchInternal);
pb2.removeObserver("", this);
//stop the rotating timer
if (this._rotateTimer)
this._rotateTimer.cancel();
//clear variables
this._obSvc = null;
this._items = null;
this._rotateTimer = null;
this._flushTimer = null;
this._isBatch = null;
this._isRotating = null;
this._isLoading = null;
this._flushPending = null;
this._defaultPrefs = null;
},
////////////////////////////////
// ffIProfileService
/**
* Currently selected profile item.
*/
get current() { return this._ensureItem(); },
set current(aVal) {
//do nothing if null passed in
if (!aVal)
return;
//do nothing if we don't have it
if (!this.hasItem(aVal.ID))
return;
//load the items properties
this._items[aVal.ID] = aVal.clone();
this._loadItem(aVal);
//notify observers
this._obSvc.notifyObservers(aVal, "forecastfox-profiles", "current");
},
/**
* Profile service is in batch mode.
*/
get isBatch() { return this._isBatch; },
/**
* Profile service is in rotating mode.
*/
get isRotating() { return this._isRotating; },
/**
* Profile is being loaded into preferences.
*/
get isLoading() { return this._isLoading; },
/**
* Profiles has a specific item.
*
* @param ID of the profile item.
* @return True if item exists.
*/
hasItem: function ProfileService_hasItem(aID)
{
return this._items.hasOwnProperty(aID);
},
/**
* Retrieve a profile item returns null if item doesn't exist.
*
* @param ID of the profile item.
* @return A ffIProfileItem.
*/
getItem: function ProfileService_getItem(aID)
{
if (!this.hasItem(aID))
return null;
else
return this._items[aID];
},
/**
* Retrieve an array of profile items.
*
* @param Count of items in the array.
* @return An array of ffIProfileItem components.
*/
getItems: function ProfileService_getItems(aCount)
{
//loop through items
var items = [];
for (var id in this._items)
items.push(this._items[id]);
//sort the array
items.sort(sortByName);
//return the array
aCount.value = items.length;
return items;
},
/**
* Sets a profile item.
*
* @param The profile item.
*/
setItem: function ProfileService_setItem(aItem)
{
//set the item
this._items[aItem.ID] = aItem.clone();
//if the item is current then load it
if (!this.isBatch && this.current.ID == aItem.ID)
this._loadItem(aItem);
//flush async
this._flushData();
//notify observers
this._obSvc.notifyObservers(this, "forecastfox-profiles", "setItem");
},
/**
* Remove a profile item. It also removes that profiles data cache.
*
* @param ID of the profile item.
*/
deleteItem: function ProfileService_deleteItem(aID)
{
//do nothing if we don't have item
if (!this.hasItem(aID))
return;
//if the item is the current then load the next one
if (!this.isBatch && this.current.ID == aID)
this.current = this._nextItem();
//remove the item
delete this._items[aID];
//remove the items cache
var mgrSvc = Cc["@ensolis.com/forecastfox/manager-service;1"].
getService(Ci.ffIManagerService);
var dskSvc = mgrSvc.disk;
var feed = dskSvc.get("feed-" + aID + ".js", TYPE_CACHE);
var radar = dskSvc.get("radar-" + aID + ".gif", TYPE_CACHE);
try {
removeFile(feed);
removeFile(radar);
} catch(e) {}
//flush async
this._flushData();
//notify observers
this._obSvc.notifyObservers(this, "forecastfox-profiles", "deleteItem");
},
/**
* Turn on batch mode. Observers will get a notification of this.
* They will still get notified for every individual change, but they are
* free to ignore those notifications.
* Use this when a lot of changes are about to happen, and it would be
* useless to refresh the display (or the backend store) for every change.
* Caller must make sure to also call endBatch. Make sure all errors
* are caught!
*/
startBatch: function ProfileService_startBatch()
{
//do nothing if already batch
if (this.isBatch)
return;
//stop rotating
this.endRotating();
//mark batch
this._isBatch = true;
//notify observers
this._obSvc.notifyObservers(this, "forecastfox-profiles", "startBatch");
},
/**
* Turn off batch mode.
*/
endBatch: function ProfileService_endBatch()
{
//do nothing if not batch
if (!this.isBatch)
return;
//make sure we have a current profile
if (!this.hasItem(getPref("profile.current")))
this.current = this._ensureItem();
else
this._loadItem(this.current);
//mark not batch
this._isBatch = false;
// flush any pending changes
if (this._flushPending) {
this._flushData();
this._flushPending = false;
}
//notify observers
this._obSvc.notifyObservers(this, "forecastfox-profiles", "endBatch");
//start rotating
this.startRotating();
},
/**
* Turn on rotating mode.
*/
startRotating: function ProfileService_startRotating()
{
//do nothing if already rotating
if (this.isRotating)
return;
//mark rotating
this._isRotating = true;
//start the timer
this._scheduleTimer();
},
/**
* Turn off rotating mode.
*/
endRotating: function ProfileService_endRotating()
{
//do nothing if not rotating
if (!this.isRotating)
return;
//mark not rotating
this._isRotating = false;
//stop the timer
this._rotateTimer.cancel();
},
/**
* Create a profile item from a DOM node.
*
* @param The profile DOM node.
* @return A profile item.
*/
createItem: function ProfileService__createItem(aNode)
{
//create an empty profile item
var item = Cc["@ensolis.com/forecastfox/profile-item;1"].
createInstance(Ci.ffIProfileItem);
//set the name
if (aNode.hasAttribute("name"))
item.setProperty("name", aNode.getAttribute("name"));
else
item.setProperty("name", aNode.getAttribute("id"));
//set the ID
item.setProperty("ID", aNode.getAttribute("id"));
//loop through pref nodes
var nodes = aNode.getElementsByTagName("pref");
for (var i=0; i<nodes.length; i++) {
var node = nodes[i];
//get the node attributes
var name = node.getAttribute("name");
var value = node.getAttribute("value");
var type = node.getAttribute("type");
//set the property based on type
switch (type) {
case "Int":
item.setProperty(name, Number(value));
break;
case "Bool":
item.setProperty(name, (value == "true"));
break;
case "Char":
default:
item.setProperty(name, value);
break;
}
}
//make sure any prefs not set are now
for (var pref in this._defaultPrefs) {
if (!item.hasProperty(pref))
item.setProperty(pref, this._defaultPrefs[pref]);
}
//return the item
return item;
},
/**
* Create a DOM node from a profile item.
*
* @param The profile item.
* @param The owner document of the node.
* @return The DOM node.
*/
createNode: function ProfileService_createNode(aItem, aDoc)
{
//create a profile node
var profile = aDoc.createElementNS(PROFILES_NS, "profile");
//set the name and id
profile.setAttribute("id", aItem.ID);
profile.setAttribute("name", aItem.name);
//loop through the preferences
for (var pref in this._defaultPrefs) {
//get the preference value
var value = "";
if (aItem.hasProperty(pref))
value = aItem.getProperty(pref);
else
value = this._defaultPrefs[pref];
//set the node properties
var node = aDoc.createElementNS(PROFILES_NS, "pref");
node.setAttribute("name", pref);
node.setAttribute("value", String(value));
if (typeof value == "number")
node.setAttribute("type", "Int");
else if (typeof value == "boolean")
node.setAttribute("type", "Bool");
else
node.setAttribute("type", "Char");
//append it to the profile
profile.appendChild(node);
}
//return the node
return profile;
},
/**
* Create a unique id for a profile item.
*
* @return A unique uuid.
*/
createID: function ProfileService_createID()
{
//use the uuid generator if it exists
var generator = Cc["@mozilla.org/uuid-generator;1"].
getService(Ci.nsIUUIDGenerator);
//create an id
var id = generator.generateUUID().toString();
//loop while the id is not unique
while (this.hasItem(id))
id = generator.generateUUID().toString();
//return the id
return id;
},
/**
* Recreate an item from current preferences.
*
* @return A ffIProfileItem.
*/
recreateItem: function ProfileService_recreateItem()
{
//create a new item
var item = Cc["@ensolis.com/forecastfox/profile-item;1"].
createInstance(Ci.ffIProfileItem);
//loop through prefs and set the property
for (var pref in this._defaultPrefs)
item.setProperty(pref, getPref(pref));
//set the name and id
item.setProperty("ID", getPref("profile.current"));
item.setProperty("name", item.getProperty("ID"));
//return the item
return item;
},
////////////////////////////////
// Internal Functions
/**
* Flush changes to disk.
*
* @param Call file writer in blocking mode.
*/
_flushData: function ProfileService__flushData()
{
//do nothing if in batch mode
if (this.isBatch) {
this._flushPending = true;
return;
}
//get the profiles file
var mgrSvc = Cc["@ensolis.com/forecastfox/manager-service;1"].
getService(Ci.ffIManagerService);
var dskSvc = mgrSvc.disk;
var file = dskSvc.get("profiles.xml", TYPE_PROFILE);
//do nothing if not writable
if ((!file.exists() && !file.parent.isWritable()) ||
(file.exists() && !file.isWritable())) {
dskSvc.log("Profile file is not writable: " + file.path, null, null);
return;
}
//create a new profiles document
var doc = dskSvc.create("profiles", PROFILES_DTD, PROFILES_NS);
//loop through items
for (var id in this._items) {
var item = this._items[id];
//create the node
var node = this.createNode(item, doc);
//append it to the document
doc.documentElement.appendChild(node);
}
//write to disk
dskSvc.write(file, doc, true, false);
},
/**
* Schedule the rotation timer.
*/
_scheduleTimer: function ProfileService__scheduleTimer()
{
//get the rotating prefs
var enabled = getPref("profile.switch.enabled");
var delay = getPref("profile.switch.delay");
delay *= 60*1000;
//not enabled or no delay
if (!enabled || delay == 0)
this.endRotating();
//start the timer
else
this._rotateTimer.init(this, delay, Ci.nsITimer.TYPE_ONE_SHOT);
},
/**
* Select the next profile item and schedule the timer.
*/
_nextItem: function ProfileService__nextItem()
{
//setup variables
var ids = [];
var index = 0;
var current = this.current.ID;
var found = false;
//loop through items
for (var id in this._items) {
//mark that we found the current
if (current == id)
found = true;
//increment the index
if (!found)
index++;
//save the id
ids.push(id);
}
//return the next item
index = (index + 1 < ids.length) ? index + 1 : 0;
return this._items[ids[index]];
},
/**
* Load an item into preferences.
*
* @param The item to load.
*/
_loadItem: function ProfileService__loadItem(aItem)
{
//mark we are loading
this._isLoading = true;
//loop through the preferences
for (var pref in this._defaultPrefs) {
//set the preference if the item has it
if (aItem.hasProperty(pref))
setPref(pref, aItem.getProperty(pref));
//use the default preference value
else
setPref(pref, this._defaultPrefs[pref]);
}
//set the current pref
setPref("profile.current", aItem.ID);
//mark we are finished loading
this._isLoading = false;
},
/**
* Ensure there is an item available for the current property.
*
* @return A profile item.
*/
_ensureItem: function ProfileService__ensureItem()
{
//get the current item
var item = this.getItem(getPref("profile.current"));
if (!item) {
//get all items
var items = this.getItems({});
//set to the first one
if (items.length > 0)
item = items[0];
//recreate it from preferences
else {
item = this.recreateItem();
this.setItem(item);
}
}
//return the item
return item;
}
};